package com.yyfax.account.service;

import com.jaws.core.common.log.Log;
import com.jaws.core.common.util.DateUtil;
import com.yyfax.account.common.annotation.AccountTradeAnno;
import com.yyfax.account.common.enums.TableNameEnum;
import com.yyfax.account.common.enums.TableSequenceEnum;
import com.yyfax.account.common.enums.TradeTypeEnum;
import com.yyfax.account.common.exception.AccountExceptionCode;
import com.yyfax.account.common.util.TableSegmentUtils;
import com.yyfax.account.dao.AccountFlowDao;
import com.yyfax.account.dao.AccountInfoDao;
import com.yyfax.account.dao.AccountTradeFrozenDao;
import com.yyfax.account.domain.AccountFlow;
import com.yyfax.account.domain.AccountInfo;
import com.yyfax.account.domain.AccountTradeFrozen;
import com.yyfax.account.vo.ComplexTradeReq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;

/**
 * 同时增加账面余额和冻结余额 服务实现类
 *
 * @author liangqm
 * @date 2018/1/16 14:00
 * @since v1.0.0
 */
@AccountTradeAnno(TradeTypeEnum.ADD_FROZEN)
@Service
public class TradeFrozenServiceImpl extends BaseTradeService<ComplexTradeReq> {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	@Resource
	private AccountInfoDao accountInfoDao;

	@Resource
	private AccountTradeFrozenDao tradeFrozenDao;

	@Resource
	private AccountFlowDao accountFlowDao;

	@Override
	public void checkBusiness(ComplexTradeReq req) {

		// 1.金额必须为整数
		if (req.getAmount() <= 0) {
			throw AccountExceptionCode.UNFROZEN_AMOUNT_NEGATIVE.exp(req.getTradeNo());
		}

		// 2.检查用户已开户
		AccountInfo account = accountInfoDao.queryAccountInfoByUser(req.getUserId());
		if (account == null || !account.getAccountNo().equals(req.getAccountNo())) {
			throw AccountExceptionCode.ACCOUNT_NOT_EXSITS.exp(req.getAccountNo());
		}
	}

	@Override
	public void batchCheckBusiness(ComplexTradeReq req, AccountInfo account) {
		final String op = "TradeFrozenServiceImpl.batchCheckBusiness";

		if (account == null || !account.getAccountNo().equals(req.getAccountNo())) {
			logger.error(Log.op(op).msg("账户不存在").kv("tradeNo", req.getTradeNo()).kv("accountNo", req.getAccountNo()).toString());
			throw AccountExceptionCode.ACCOUNT_NOT_EXSITS.exp(req.getAccountNo());
		}

		if (req.getAmount() <= 0) {
			logger.error(Log.op(op).msg("交易金额必须大于0").kv("tradeNo", req.getTradeNo()).kv("amount", req.getAmount()).toString());
			throw AccountExceptionCode.FROZEN_AMOUNT_NEGATIVE.exp(req.getAmount(), req.getTradeNo());
		}
	}

	@Transactional
	@Override
	public void doTrade(ComplexTradeReq req, Boolean isBatch) {
		final String op = "TradeFrozenServiceImpl.doTrade";
		logger.info(Log.op(op).msg("同时增加冻结和账面余额开始").kv("req", req).toString());

		// 1.业务检查
		if (!isBatch) {
			checkBusiness(req);
		}

		// 2.保存交易订单
		String segment = TableSegmentUtils.getTableSegment(TableNameEnum.ACCOUNT_TRADE_FROZEN, req.getUserId());
		// 唯一约束冲突标识位
		boolean duplicateKeyFlag = false;

		AccountTradeFrozen tradeFrozen = buildTradeFrozen(req);
		try {
			tradeFrozenDao.insert(tradeFrozen, segment);
		} catch (DuplicateKeyException e) {
			logger.info(Log.op(op).msg("唯一约束冲突").kv("tradeNo", req.getTradeNo()).toString(), e);
			duplicateKeyFlag = true;
		}

		// out_trade_no唯一索引冲突，trade_no唯一约束冲突
		if (duplicateKeyFlag) {
			AccountTradeFrozen existOrder = tradeFrozenDao.queryOrderByOutTradeNo(req.getTradeNo(), segment);
			if (existOrder == null) {
				throw AccountExceptionCode.SYS_ERROR.exp();
			}
			return;
		}

		// 3.驱动账户变更
		int result = accountInfoDao.updateTradeFrozen(req.getUserId(), req.getAccountNo(), req.getAmount());

		if (result != 1) {
			throw AccountExceptionCode.AMOUNT_NOT_ENOUGH.exp(req.getTradeNo(), req.getUserId(), req.getAmount());
		}

		// 4.生成账户流水
		if (!CollectionUtils.isEmpty(req.getFlowDataLs())) {
			List<AccountFlow> accountFlows = buildAccountFlow(req);
			for (AccountFlow accountFlow : accountFlows) {
				accountFlowDao.insert(accountFlow, segment);
			}
		}
		logger.info(Log.op(op).msg("同时增加冻结和账面余额结束").kv("req", req).toString());
	}

	private AccountTradeFrozen buildTradeFrozen(ComplexTradeReq req) {
		AccountTradeFrozen po = new AccountTradeFrozen();
		po.setUserId(req.getUserId());
		po.setAccountNo(req.getAccountNo());
		po.setAmount(req.getAmount());
		po.setTradeNo(commonIdUtils.generateId(TableSequenceEnum.ACCOUNT_TRADE_FROZEN));
		po.setOutTradeNo(req.getTradeNo());
		po.setOutBusinessNo(req.getBusinessNo());
		po.setOrignOutTradeNo("");
		po.setTradeTime(req.getTradeTime());
		po.setTradeType(req.getTradeType().getCode());
		po.setSource(req.getSource().getCode());
		po.setRemark(req.getRemark());
		po.setCreateTime(DateUtil.getCurrentDateTime());
		po.setUpdateTime(DateUtil.getCurrentDateTime());
		return po;
	}
}
